package me.flyray.bsin.server.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import me.flyray.bsin.blockchain.config.S11eProtocolProperties;
import me.flyray.bsin.blockchain.connection.BsinBlockChainEngineFactory;
import me.flyray.bsin.blockchain.dto.ContractTransactionResp;
import me.flyray.bsin.blockchain.protocol.S11eProtocolService;
import me.flyray.bsin.constants.ResponseCode;
import me.flyray.bsin.context.BsinServiceContext;
import me.flyray.bsin.domain.entity.*;
import me.flyray.bsin.domain.enums.AssetsCollectionType;
import me.flyray.bsin.exception.BusinessException;
import me.flyray.bsin.facade.request.DigitalAssetsIssueReqDTO;
import me.flyray.bsin.facade.service.DigitalAssetsCollectionService;
import me.flyray.bsin.facade.engine.DigitalPointsServiceEngine;
import me.flyray.bsin.infrastructure.biz.*;
import me.flyray.bsin.infrastructure.mapper.*;
import me.flyray.bsin.security.contex.LoginInfoContextHelper;
import me.flyray.bsin.security.domain.LoginUser;
import me.flyray.bsin.utils.BsinSnowflake;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shenyu.client.apache.dubbo.annotation.ShenyuDubboService;
import org.apache.shenyu.client.apidocs.annotations.ApiDoc;
import org.apache.shenyu.client.apidocs.annotations.ApiModule;
import org.apache.shenyu.client.dubbo.common.annotation.ShenyuDubboClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

/**
 * @author bolei
 * @date 2023/7/3 15:49
 * @desc
 */

@Slf4j
@ShenyuDubboService(path = "/digitalPoints", timeout = 6000)
@ApiModule(value = "digitalPoints")
public class DigitalPointsServiceImpl implements DigitalPointsServiceEngine {

  @Autowired private ContractProtocolMapper contractProtocolMapper;
  @Autowired private DigitalAssetsBiz digitalAssetsBiz;
  @Autowired private DigitalAssetsCollectionMapper digitalAssetsCollectionMapper;
  @Autowired private TokenParamMapper tokenParamMapper;
  @Autowired private DigitalAssetsItemBiz digitalAssetsItemBiz;
  @Autowired private MerchantInfoBiz merchantInfoBiz;
  @Autowired private CrmAccountBiz crmAccountBiz;
  @Autowired private DigitalAssetsCollectionService digitalAssetsCollectionService;
  @Autowired
  private S11eProtocolService s11eProtocolService;
  @Autowired
  private WalletMapper walletMapper;
  @Autowired
  private WalletAccountMapper walletAccountMapper;
  @Autowired
  private ChainCoinMapper chainCoinMapper;
  @Autowired
  private BsinBlockChainEngineFactory bsinBlockChainEngineFactory;
  @Autowired
  private DidProfileMapper didProfileMapper;
  @Autowired
  private S11eProtocolProperties s11eConfig;

  @Value("${bsin.jiujiu.aesKey}")
  private String aesKey;

  /**
   * 注：租户平台通过直属商户发行积分，解决积分商城和功能共用问题
   * 开通商户的积分账户，商户可以选择激励的数字资产，开通对应数字积分账户，通过币种关联数字资产中心
   * 1.获取发行商信息
   * 2.账户余额判断
   * 3.根据protocolCode和chainType 获取 contractProtocol
   * 4.部署合约
   * 5.账户扣费
   * 6.写入资产信息
   * 7.插入token基础信息
   * @param requestMap
   * @return
   * @throws Exception
   */
  @ShenyuDubboClient("/issue")
  @ApiDoc(desc = "issue")
  @Override
  @Transactional
  public void issue(Map<String, Object> requestMap) throws Exception {
    log.info("DigitalPointsService issue 请求参数:{}", JSON.toJSONString(requestMap));
    Boolean onChainFlag = MapUtils.getBoolean(requestMap, "onChainFlag");
    // 发行商
    LoginUser loginUser = LoginInfoContextHelper.getLoginUser();

    String merchantNo = StringUtils.defaultIfBlank(
        loginUser.getMerchantNo(), LoginInfoContextHelper.getTenantMerchantNo());
    if (merchantNo == null) {
      throw new BusinessException(ResponseCode.MERCHANT_NO_IS_NULL);
    }

    String tenantId = loginUser.getTenantId();
    DigitalAssetsIssueReqDTO digitalAssetsIssueReqDTO =
        BsinServiceContext.getReqBodyDto(DigitalAssetsIssueReqDTO.class, requestMap);

    String chainEnv = digitalAssetsIssueReqDTO.getChainEnv();
    String chainType = digitalAssetsIssueReqDTO.getChainType();
    // 合约是否被赞数
    String sponsorFlag = digitalAssetsIssueReqDTO.getSponsorFlag();

    String contractProtocolNo = MapUtils.getString(requestMap, "contractProtocolNo");
    String protocolCode = MapUtils.getString(requestMap, "protocolCode");
    if (protocolCode == null && contractProtocolNo == null) {
      throw new BusinessException("100000", "protocolCode或contractProtocolNo为空！！！");
    }

    // 1.发行商账户余额判断
    // crmAccountBiz.checkAccountBalance(issuerBase, chainType, chainEnv);

    // 2.根据protocolCode和chainType 获取 contractProtocol
    ContractProtocol contractProtocol =
            digitalAssetsItemBiz.getContractProtocol(
                    digitalAssetsIssueReqDTO, protocolCode, contractProtocolNo);

    // 3.部署合约
    if(onChainFlag){
        // 获取发行商信息
        Map issuerBase = merchantInfoBiz.getMerchantInfo(merchantNo, chainType);
        digitalAssetsIssueReqDTO.setPrivateKey((String) issuerBase.get("privateKey"));
        digitalAssetsIssueReqDTO.setOwnerAddress((String) issuerBase.get("walletAddress"));

        // 查询商户铸造的品牌，基于品牌信息铸造积分
        // 找到链上品牌
        DidProfile didProfile = didProfileMapper.selectOne(new LambdaQueryWrapper<DidProfile>().eq(DidProfile::getBizRoleTypeNo, merchantNo));

        // 查询品牌的钱包账户，初始化钱包账户
        // 根据 MapUtils.getString(requestMap, "chainIdentifier") 获取链本币
        String chainIdentifier = didProfile.getChainIdentifier();
        String nativeCoin = bsinBlockChainEngineFactory.getChainNativeCoin(chainIdentifier);
        log.info("准备用链上资料，链标识:{}，链本币:{}", chainIdentifier, nativeCoin);
        // 根据 chainIdentifier 查询币种
        ChainCoin chainNativeCoin = chainCoinMapper.selectOne(new LambdaQueryWrapper<ChainCoin>()
                .eq(ChainCoin::getChainIdentifier, chainIdentifier)
                .eq(ChainCoin::getCoin, nativeCoin));
        log.debug("链本币查询结果: {}", chainNativeCoin);

        // 查询商户钱包
        LambdaQueryWrapper<Wallet> walletQueryWrapper = new LambdaQueryWrapper<>();
        walletQueryWrapper.eq(Wallet::getBizRoleTypeNo, merchantNo);
        Wallet wallet = walletMapper.selectOne(walletQueryWrapper);
        log.debug("查询商户:{} 对应钱包: {}", merchantNo, wallet);

        LambdaQueryWrapper<WalletAccount> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(WalletAccount::getWalletNo, wallet.getSerialNo());
        queryWrapper.eq(WalletAccount::getChainCoinNo, chainNativeCoin.getSerialNo());
        WalletAccount walletAccount = walletAccountMapper.selectOne(queryWrapper);
        log.debug("链本币账户: {}", walletAccount);

        if (walletAccount == null) {
          throw new BusinessException("WAAS_WALLET_ACCOUNT_MISSING",
                  "商户钱包账户不存在，无法发行PassCard");
        }

        Map<String, Object> request = new HashMap<>();
        request.put("chainIdentifier", chainIdentifier);
        request.put("chainEnv", chainEnv);
        request.put("privateKey", walletAccount.getPrivateKey());
        request.put("profileAddress", didProfile.getContractAddress());
        request.put("assetFactoryAddress", s11eConfig.getContracts().getAssetFactory());

        // DigitalPoints 参数
        request.put("name", digitalAssetsIssueReqDTO.getName());
        request.put("symbol", digitalAssetsIssueReqDTO.getSymbol());
        request.put("assetOwner", didProfile.getContractAddress());

        s11eProtocolService.issueDigitalPoints(requestMap);

//        ContractTransactionResp contractTransactionResp = digitalAssetsBiz.deployContract(digitalAssetsIssueReqDTO);
//        log.info("DigitalAssetsService issue 相应结果:{}", JSON.toJSONString(contractTransactionResp));
        // 5.账户扣费
        crmAccountBiz.accountOut(issuerBase, chainEnv);
    }

    // 6.写入资产信息
    DigitalAssetsCollection digitalAssetsColletion = new DigitalAssetsCollection();
    BeanUtil.copyProperties(digitalAssetsIssueReqDTO, digitalAssetsColletion);
    digitalAssetsColletion.setMerchantNo(merchantNo);
    digitalAssetsColletion.setSerialNo(BsinSnowflake.getId());
    digitalAssetsColletion.setContractAddress("contractTransactionResp.getContractAddress()");
    digitalAssetsColletion.setSponsorFlag(sponsorFlag);
    digitalAssetsColletion.setCreateBy(merchantNo);
    digitalAssetsColletion.setContractProtocolNo(contractProtocol.getSerialNo());
    digitalAssetsColletion.setName((String) requestMap.get("name"));

    if (digitalAssetsIssueReqDTO.getDecimals() != null) {
      digitalAssetsColletion.setDecimals(digitalAssetsIssueReqDTO.getDecimals());
    } else {
      digitalAssetsColletion.setDecimals(0);
    }

    BigDecimal melo =
        new BigDecimal(Math.pow(10, digitalAssetsColletion.getDecimals().doubleValue()));
    // 初始供应量
    if (digitalAssetsIssueReqDTO.getInitialSupply() != null) {
      digitalAssetsColletion.setInitialSupply(
          digitalAssetsIssueReqDTO.getInitialSupply().multiply(melo));
    } else {
      digitalAssetsColletion.setInitialSupply(new BigDecimal("0"));
    }
    // 总供应量
    digitalAssetsColletion.setTotalSupply(
        new BigDecimal((String) requestMap.get("totalSupply")).multiply(melo));

    // 库存
    BigDecimal inventory =
        digitalAssetsColletion.getTotalSupply().subtract(digitalAssetsColletion.getInitialSupply());
    digitalAssetsColletion.setInventory(inventory);

    digitalAssetsColletion.setTenantId(tenantId);
    digitalAssetsColletion.setChainEnv(chainEnv);
    digitalAssetsColletion.setChainType(contractProtocol.getChainType());
    digitalAssetsColletion.setAssetsType(digitalAssetsIssueReqDTO.getAssetsType());
    digitalAssetsCollectionMapper.insert(digitalAssetsColletion);

    // 7.插入token基础信息
    TokenParam tokenParam = new TokenParam();
    tokenParam.setSerialNo(BsinSnowflake.getId());
    tokenParam.setTenantId(loginUser.getTenantId());
    tokenParam.setMerchantNo(merchantNo);
    tokenParam.setDigitalAssetsCollectionNo(digitalAssetsColletion.getSerialNo());
    tokenParam.setTotalSupply(digitalAssetsColletion.getTotalSupply());
    tokenParam.setDecimals(digitalAssetsColletion.getDecimals());
    tokenParam.setCirculation(digitalAssetsColletion.getInitialSupply());
    tokenParam.setName(digitalAssetsColletion.getName());
    tokenParam.setSymbol(digitalAssetsColletion.getSymbol());
    tokenParamMapper.insert(tokenParam);

  }

  @ShenyuDubboClient("/getDetailByMerchantNo")
  @ApiDoc(desc = "getDetailByMerchantNo")
  @Override
  public DigitalAssetsCollection getDetailByMerchantNo(Map<String, Object> requestMap) {
    String merchantNo = MapUtils.getString(requestMap, "merchantNo");
    DigitalAssetsCollection digitalAssetsCollection =
        digitalAssetsCollectionMapper.selectOne(
            new LambdaQueryWrapper<DigitalAssetsCollection>()
                .eq(DigitalAssetsCollection::getMerchantNo, merchantNo)
                .eq(
                    DigitalAssetsCollection::getAssetsType,
                    AssetsCollectionType.DIGITAL_POINT.getCode()));
    return digitalAssetsCollection;
  }

  /**
   * 在链上mint积分
   * @param requestMap
   * @throws Exception
   */
  @ShenyuDubboClient("/mint")
  @ApiDoc(desc = "mint")
  @Override
  public void mint(Map<String, Object> requestMap) throws Exception {

    // 查询平台或商户的数字积分信息
    String toAddress = MapUtils.getString(requestMap, "toAddress");
    String amount = MapUtils.getString(requestMap, "amount");

    // 查询商户发行的积分合约地址

    Map<String, Object> request = new HashMap<>();
    request.put("chainIdentifier", "TEST_CHAIN_IDENTIFIER");
    request.put("chainEnv", "TEST_CHAIN_ENV");
    request.put("privateKey", "");
    request.put("digitalPointsAddress", "newDigitalPointsAddress");
    request.put("to", toAddress);
    request.put("amount", amount);  // 铸造 1000 积分

    s11eProtocolService.mintDigitalPoints(requestMap);

    digitalAssetsCollectionService.mint(requestMap);
  }

  /**
   * 将劳动价值转化成曲线价值，曲线价值转换成数字积分
   * @param requestMap
   * @throws Exception
   */
  @ShenyuDubboClient("/mintByLaborValue")
  @ApiDoc(desc = "mintByLaborValue")
  public void mintByLaborValue(Map<String, Object> requestMap) throws Exception {

  }


  /**
   * 在链上mint积分
   * @param requestMap
   * @throws Exception
   */
  @ShenyuDubboClient("/release")
  @ApiDoc(desc = "release")
  @Override
  public void release(Map<String, Object> requestMap) throws Exception {

    // 计算每个人应该得到多少积分，根据结果为每个人进行铸造

    digitalAssetsCollectionService.mint(requestMap);
  }

  /**
   * 根据劳动价值计算应该激励的数字积分
   * 1、计算劳动价值所有铸造量：根据实际贡献价值，计算联合曲线上的应得价值
   * 2、基于应得价值去铸铸造链上数字积分
   * @throws Exception
   */
  @ShenyuDubboClient("/calculateValueOfLabor")
  @ApiDoc(desc = "calculateValueOfLabor")
  @Override
  public BigDecimal calculateValueOfLabor(Integer bondingCurveValue) throws Exception {
    return BigDecimal.ZERO;
  }

}
